import { extend } from '../shared/index'

let activeEffect
let shouldTrack // stop之后，若执行prop = prop+1，则又会重新收集依赖，这不是我们期望的，故加这个字段判断是否需要收集依赖
export class ReactiveEffect {
  private _fn: any
  deps = []
  active = true //优化stop性能
  onStop?: () => void
  constructor(fn, public scheduler?) {
    this._fn = fn
  }

  run() {
    //1.会收集依赖
    // shouldTrack 来做区分
    if (!this.active) {
      return this._fn()
    }
    shouldTrack = true
    activeEffect = this
    const result = this._fn()
    //reset
    shouldTrack = false
    return result
  }
  stop() {
    if (this.active) {
      cleanupEffect(this)
      if (this.onStop) {
        this.onStop()
      }
      this.active = false
    }
  }
}
function cleanupEffect(effect) {
  effect.deps.forEach((dep: any) => {
    dep.delete(effect)
  })
  effect.deps.length = 0;
}

const targetMap = new Map()
export function track(target, key) {
  // target -> key -> dep
  if (!isTracking()) return;

  let depsMap = targetMap.get(target)
  if (!depsMap) {
    depsMap = new Map()
    targetMap.set(target, depsMap)
  }
  let dep = depsMap.get(key)
  if (!dep) {
    dep = new Set()
    depsMap.set(key, dep)
  }
  trackEffects(dep);
  
}

export function trackEffects(dep){
  // 已经在 dep 中
  if (dep.has(activeEffect)) return;
  
  dep.add(activeEffect)
  activeEffect.deps.push(dep)
}

export function isTracking() {
  return shouldTrack && activeEffect !== undefined;
  
}
export function trigger(target, key) {
  let depsMap = targetMap.get(target)
  let dep = depsMap.get(key)

  triggerEffects(dep);
}
export function triggerEffects(dep){
  
  for (const effect of dep) {
    if (effect.scheduler) {
      effect.scheduler()
    } else {
      effect.run()
    }
  }
  // dep.forEach(effect=>{
  //    if (effect.scheduler) {
  //     effect.scheduler()
  //   } else {
  //     effect.run()
  //   }
  // })
}

export function effect(fn, options: any = {}) {
  // fn
  const _effect = new ReactiveEffect(fn, options.scheduler)
  // options
  // extend
  extend(_effect, options)

  _effect.run()

  //bind 处理this指向到实例
  const runner: any = _effect.run.bind(_effect)
  runner.effect = _effect
  return runner
}

export function stop(runner) {
  runner.effect.stop()
}
